﻿/**** Versão Izabel tentando colocar direção *****/

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

using LuaInterface;
using System.Collections;
using System.Collections.Generic;

namespace catch_me_ai {
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game:Microsoft.Xna.Framework.Game {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        List<Item> listaItens;
        Mapa mapaAtual;
        Jogador jogador;
        NPC inimigo;

        Texture2D imgIntroducao;
        Texture2D imgJogo;
        bool comecaJogo = false;

        Texture2D paredeTextura;
        Texture2D fundoTextura;
        Texture2D portaoTextura;
        Texture2D gemaTextura;
        SpriteFont fonteEstado;
        float orientacao;

        ValorItemMapa valorItemMapa;
        String mensagemErro = null;
        KeyboardState keyboardState;

        int pontuacaoJogador = 0;
        int vidasJogador;

        bool gameOver = false;
        bool winner = false;
        bool passouFase = false;
        bool isUltimaFase = false;

        // Para controle do problema ao andar na horizontal
        private bool tecladoPressionado;
        private bool andandoVertical = false;
        private bool andouParaBaixo = false;
        private bool ocorreuErroCarregamento = false;

        public Game() {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 900;
            graphics.PreferredBackBufferHeight = 600;
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize() {
            base.Initialize();

             try {
                // Seleciona a fase do jogo
                mapaAtual = new Mapa("mapas.lua");
                listaItens = mapaAtual.carregarItensMapa();

                inimigo = new NPC(mapaAtual.getPosicaoInicialNPC(), Vector2.Zero, Content.Load<Texture2D>("personagens/inimigo"), TipoEstado.Vagando);
                jogador = new Jogador(mapaAtual.getPosicaoInicialJogador(), Vector2.Zero, new Vector2(2,2), Content.Load<Texture2D>("personagens/keith"));

                inicializaValoresMapa();
             } catch (InvalidMapaException e) {
                 ocorreuErroCarregamento = true;
                 mensagemErro = e.getMensagemErro();
             }
        }


        // Inicializa a proxima fase/mapa
        public void proximaFase() {
            try {
                // Se não for a última fase
                if (!mapaAtual.getIsUltimaFase()) {
                    pontuacaoJogador = 0;
                    mapaAtual.carregarMapa();
                    listaItens = mapaAtual.carregarItensMapa();

                    inimigo = new NPC(mapaAtual.getPosicaoInicialNPC(), Vector2.Zero, Content.Load<Texture2D>("personagens/inimigo"), TipoEstado.Vagando);
                    jogador = new Jogador(mapaAtual.getPosicaoInicialJogador(), Vector2.Zero, new Vector2(2, 2), Content.Load<Texture2D>("personagens/keith"));

                    inicializaValoresMapa();
                }
            } catch (InvalidMapaException e) {
                ocorreuErroCarregamento = true;
                mensagemErro = e.getMensagemErro();
            }
        }


        // Inicializa os valores do mapa atual. Esses valores são
        // a quantidade de vidas, valores dos itens e quantos pontos
        // precisa para passar de fase. Quando não houver valores será
        // usado os valores default.
        private void inicializaValoresMapa() {
            valorItemMapa = mapaAtual.carregarConfiguracoesMapa();
            vidasJogador = valorItemMapa.getValorVD();
            isUltimaFase = mapaAtual.getIsUltimaFase();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent() {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            #region "Carrega texturas"
            imgIntroducao = Content.Load<Texture2D>("img_introducao");
            imgJogo = Content.Load<Texture2D>("img_jogo");
            paredeTextura = Content.Load<Texture2D>("mapa/preto");
            fundoTextura = Content.Load<Texture2D>("mapa/branco");
            portaoTextura = Content.Load<Texture2D>("itens/gate");
            gemaTextura = Content.Load<Texture2D>("itens/gema");
            fonteEstado = Content.Load<SpriteFont>("fonte/fonteEstado");
            #endregion "Carrega texturas"
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent() {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime) {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) {
                this.Exit();
            }

            keyboardState = Keyboard.GetState();

            
                if (ocorreuErroCarregamento)
                {
                    //fica lendo se o jogador apertou a tecla que manda reiniciar o jogo.

                }
                else // se não houve um perdedor nem ganhador
                    if (comecaJogo)
                    {
                        if (!gameOver || !winner)
                        {

                            // checa se houve colisão com o inimigo
                            // se ocorreu, perde uma vida e volta ao ponto de partida.
                            if (ocorreuColisao(inimigo.getTextura(), inimigo.getPosicao(), jogador.getTextura(), jogador.getPosicao()))
                            {
                                vidasJogador--;
                                jogador.setPosicao(mapaAtual.getPosicaoInicialJogador());
                            }

                            isGameOver();
                            moveJogador();
                            moveInimigo(gameTime);
                        }
                    }
                    else
                    {
                        if (keyboardState.IsKeyDown(Keys.Enter))
                            comecaJogo = true;

                    }

            // Reinicia/recarrega o jogo.  (Para quando mudar algo no arquivo lua)
            if (keyboardState.IsKeyDown(Keys.F5)) {
                Initialize();
            }
            base.Update(gameTime);
        }

        // Verifica se ocorreu Game Over.
        private void isGameOver() {
            if (vidasJogador <= 0 && !winner) {
                gameOver = true;
            }
        }

        // Verifica se o jogador passou de fase.
        private void isWinner() {
            if (vidasJogador <= 0) {
                winner = true;
            }
        }

        private void moveInimigo(GameTime gameTime) {
            #region "Mover inimigo"

            Vector2 posAntes = inimigo.getPosicao();
            SteerBehaviors comportamento = new SteerBehaviors();
            float secs = (float) gameTime.ElapsedGameTime.TotalSeconds;
            if (pontuacaoJogador == 0)
            {
                comportamento.wander(50, new Random().Next(20, 180), 100, inimigo, secs);
                inimigo.setEstado(TipoEstado.Vagando);
            }
            else
            {
                comportamento.persuit(jogador, inimigo, secs);
                inimigo.setEstado(TipoEstado.Perseguindo);
            }

            orientacao = (float) Math.Atan2(inimigo.getPosicao().Y - posAntes.Y,
                    inimigo.getPosicao().X - posAntes.X);

            #endregion "Mover inimigo"
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime) {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            
            if (ocorreuErroCarregamento)
            {
                //chama método para desenhar na tela uma mensagem de erro,
                // informando que o mapa/fase está maior que o tamanho máximo permitido.
                desenhaErroCarregamento();
            }
            if (comecaJogo)
            {
                spriteBatch.Draw(imgJogo, new Vector2(0, 0), Color.White);
           
                if (gameOver)
                {
                    //mostra mensagem de fim de jogo.
                    desenhaFimJogo();
                }
                else if (winner)
                {
                    //mostra mensagem de ganhador.
                    desenhaVencedor();
                }
                else
                {
                    desenhaEstados();
                    desenhaMapa();
                    desenhaPlayers();
                    desenhaInformacoes();
                }      
            }
            else
            {
                if (!ocorreuErroCarregamento)
                    spriteBatch.Draw(imgIntroducao, new Vector2(0, 0), Color.White);
            }

                base.Draw(gameTime);
            spriteBatch.End();
        }

        private void desenhaErroCarregamento() {
            if (ocorreuErroCarregamento) {
                spriteBatch.DrawString(fonteEstado, "Infelizmente nao foi possivel prosseguir com o carregamento do mapa!", new Vector2(30, 200), Color.White);
                spriteBatch.DrawString(fonteEstado, "Erros: ", new Vector2(30, 250), Color.White);
                spriteBatch.DrawString(fonteEstado, mensagemErro, new Vector2(50, 300), Color.White);
            }
        }

        private void desenhaFimJogo() {
            if (gameOver) {
                spriteBatch.DrawString(fonteEstado, "Game Over!", new Vector2(150, 150), Color.Black);
            }
        }

        private void desenhaVencedor() {
            if (winner) {
                spriteBatch.DrawString(fonteEstado, "Voce venceu!", new Vector2(150, 150), Color.Black);
            }
        }

        private void desenhaPlayers() {
            spriteBatch.Draw(inimigo.getTextura(), inimigo.getPosicao(), null, Color.White, orientacao,
                    new Vector2(inimigo.getTextura().Width / 2, inimigo.getTextura().Height / 2), 1.0f, SpriteEffects.FlipHorizontally, 0.0f);
            spriteBatch.Draw(jogador.getTextura(), jogador.getPosicao(), Color.White);
        }

        private void desenhaInformacoes() {
            spriteBatch.DrawString(fonteEstado, "Vidas: " + vidasJogador.ToString(), new Vector2(500, 200), Color.White);
            spriteBatch.DrawString(fonteEstado, "Pontuacao: " + pontuacaoJogador.ToString(), new Vector2(500, 250), Color.White);
            spriteBatch.DrawString(fonteEstado, "Pontos Necessarios: " + valorItemMapa.getValorPN().ToString(), new Vector2(500, 300), Color.White);
        }

        private void desenhaEstados() {
            switch (inimigo.getEstado()) {
                case TipoEstado.Vagando:
                    spriteBatch.DrawString(fonteEstado, "Vagando", new Vector2(600, 100), Color.White);
                    break;
                case TipoEstado.Perseguindo:
                    spriteBatch.DrawString(fonteEstado, "Perseguindo", new Vector2(600, 100), Color.White);
                    break;
            }
        }

        private void desenhaMapa() {
            #region "Mapa - Desenhando mapa"

            for (int i = 0; i < listaItens.Count; i++) {
                Texture2D textura;
                Item item = listaItens[i];
                if (item.getTipoItem() == TipoItem.Parede) {
                    textura = paredeTextura;
                    spriteBatch.Draw(textura, item.getPosicao(), Color.White);
                }

                if (item.getTipoItem() == TipoItem.Diamante) {
                    textura = gemaTextura;
                    spriteBatch.Draw(textura, item.getPosicao(), Color.White);
                }

                if (item.getTipoItem() == TipoItem.Portao) {
                    textura = portaoTextura;
                    spriteBatch.Draw(textura, item.getPosicao(), Color.White);
                }
            }

            #endregion "Mapa"
        }

        private void moveJogador() {
            // Para controle do problema ao andar na horizontal
            bool andandoHorizontal = false;
            tecladoPressionado = false;

            // Recebe o estado do teclado
            KeyboardState keyboardState = Keyboard.GetState();
            Vector2 direcao = jogador.getDirecao();

            // Verifica se o jogador pressionou seta para cima
            if (keyboardState.IsKeyDown(Keys.Up)) {
                direcao = new Vector2(0, -1);
                tecladoPressionado = true;

                // Para controle do problema ao andar na horizontal
                andandoVertical = true;
            }
            // Verifica se o jogador pressionou seta para baixo
            if (keyboardState.IsKeyDown(Keys.Down)) {
                direcao = new Vector2(0, 1);
                tecladoPressionado = true;

                // Para controle do problema ao andar na horizontal
                andandoVertical = true;
                andouParaBaixo = true;
            }
            // Verifica se o jogador pressionou seta para direita
            if (keyboardState.IsKeyDown(Keys.Right)) {
                direcao = new Vector2(1, 0);
                tecladoPressionado = true;

                // Para controle do problema ao andar na horizontal
                andandoHorizontal = true;
            }
            // Verifica se o jogador pressionou seta para esqueda
            if (keyboardState.IsKeyDown(Keys.Left)) {
                direcao = new Vector2(-1, 0);
                tecladoPressionado = true;

                // Para controle do problema ao andar na horizontal
                andandoHorizontal = true;
            }

            if (tecladoPressionado) {
                bool colidiu = false;
                // Atualiza a posição do personagem
                Vector2 posicao = jogador.getPosicao() + direcao * jogador.getVelocidade();

                //Corrige problema ao andar na horizontal que fazia o jogador ficar desalinhado.
                if (andandoHorizontal && andandoVertical) {
                    if (andouParaBaixo) {
                        posicao.Y = posicao.Y + 1.0f;
                        andouParaBaixo = false;
                    } else {
                        posicao.Y = posicao.Y - 1.0f;
                    }
                    andandoHorizontal = false;
                    andandoVertical = false;
                }

                // Checa se ocorreu colisão com algum item
                for (int pos = 0; pos < listaItens.Count; pos++) {
                    Item item = listaItens[pos];

                    if (item.getTipoItem() == TipoItem.Parede) {
                        if (ocorreuColisao(paredeTextura, item.getPosicao(), jogador.getTextura(), posicao)) {
                            colidiu = true;
                            break;
                        }
                    }

                    if (item.getTipoItem() == TipoItem.Diamante) {
                        if (ocorreuColisao(gemaTextura, item.getPosicao(), jogador.getTextura(), posicao)) {
                            coletaItem(TipoItem.Diamante, item.getPosicao(), pos);
                            break;
                        }
                    }

                    if (item.getTipoItem() == TipoItem.Portao) {
                        if (ocorreuColisao(portaoTextura, item.getPosicao(), jogador.getTextura(), posicao)){
                            if (!item.getPassavel()) {
                                colidiu = true;
                                break;
                            } else {
                                if (isUltimaFase) {
                                    winner = true;
                                    break;
                                } else {
                                    passouFase = true;
                                    proximaFase();
                                }
                            }
                        }
                    }
                }

                if (!colidiu) {
                    jogador.setPosicao(posicao);
                    jogador.setDirecao(direcao);
                }
            }
        }

        // Pega/recolhe o item pelo qual o jogador passou por cima.
        private void coletaItem(TipoItem tipoItem, Vector2 posicaoGema, int posicaoLista) {
            if (tipoItem == TipoItem.Diamante) {
                controlaPontuacao(valorItemMapa.getValorID());
            }

            listaItens.RemoveAt(posicaoLista);
        }

        // Soma pontos ao jogador e verifica se já pode abrir os portões
        private void controlaPontuacao(int valorPontos) {
            pontuacaoJogador += valorPontos;

            if (pontuacaoJogador >= valorItemMapa.getValorPN()) {
                abrePortoes();
            }
        }

        // Abre os portões para o jogador
        private void abrePortoes() {
            for (int pos = 0; pos < listaItens.Count; pos++) {
                Item item = listaItens[pos];

                if (item.getTipoItem() == TipoItem.Portao) {
                    item.setPassavel(true);
                }
            }
        }

        // Checa se ocorreu colisão entre dois objetos
        public bool ocorreuColisao(Texture2D imagemObstaculo, Vector2 posicaoObstaculo, Texture2D imagemPersonagem, Vector2 posicaoPersonagem) {
            Rectangle player = new Rectangle((int) posicaoPersonagem.X, (int) posicaoPersonagem.Y, (int) imagemPersonagem.Width, (int) imagemPersonagem.Height);
            Rectangle obstaculo = new Rectangle((int) posicaoObstaculo.X, (int) posicaoObstaculo.Y, (int) imagemObstaculo.Width, (int) imagemObstaculo.Height);

            if (obstaculo.Intersects(player)) {
                return true;
            }

            return false;
        }

    }
}